//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
// --------------------------------------------------------------
//      GEANT 4 - main
//
// --------------------------------------------------------------

#include <vector>
#include <string>
#include <algorithm>
#include <regex>
#include <sys/stat.h>

#include "MyPhysicsList.hh"
#include "MyActionInitialization.hh"
#include "MyAnalysisManager.hh"
#include "MyDetectorConstruction.hh"
#include "MyGenContainer.hh"
#include "MyRunManager.hh"

//#include "G4RunManager.hh"
#include "G4UImanager.hh"
#include "G4VisExecutive.hh"
#include "G4UIExecutive.hh"
#include "G4GDMLParser.hh"
#include "G4VModularPhysicsList.hh"
#include "G4StepLimiterPhysics.hh"
#include "G4ThreeVector.hh"
#include "Randomize.hh"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#include "TRandom.h"
#include "TSystem.h"
#include "TString.h"
#include "TEnv.h"
#pragma GCC diagnostic pop

#include "yaml-cpp/yaml.h"

#include "QBBC.hh"

//template 缺省值：
// 1. 关闭verbose输出，关闭material输出
// 2. 采用particle gun，1GeV/c，电子，(1,0,0)方向出射
// 3. 开启光学过程，采用G4OpticalPhysics
// 4. 若用MyRadiatorDescription定义了穿越辐射探测器结构，则开启TRD物理过程
// 5. 若用XML开启了磁场，则用用户定义的磁场；否则用全局磁场
// 6. 采用G4AnalysisManager保存数据

//marvelSim 设置：
//

//#Verb 1. 打开输出verbose控制
G4int verbose = 0;

//arglist control variables
struct BashCommands
{
    int RandomSeed;
    std::string OutputFile;
    std::string DetectorGDML;
    BashCommands()
    {
        RandomSeed = -1;
        OutputFile = "";
        DetectorGDML = "";
    };
};

BashCommands bashComs;

void PrintUsage()
{
    G4cout << "**************************************************************" << G4endl;
    G4cout << "**************************************************************" << G4endl;
    G4cout << "********** Welcome to Star-XP Simulation Framework! **********" << G4endl;
    G4cout << "********** Usage: ./marvelSim [-v] [SimuCard.card]   **********" << G4endl;
    G4cout << "**************************************************************" << G4endl;
    G4cout << "**************************************************************" << G4endl;
}

bool IfFileExist(G4String FilePath)
{
    struct stat buffer;   
    return (stat(FilePath.data(), &buffer) == 0);
}

std::vector<std::string> SplitStr(const char* Str, const char* Split)
{
    std::string VStr(Str);
    std::vector<std::string> Vector;
    std::regex Delimiter(Split);
    std::sregex_token_iterator iter(VStr.begin(), VStr.end(), Delimiter, -1);
    std::sregex_token_iterator end;
    while (iter != end)
    {   
        Vector.push_back(std::string(*iter));
        ++iter;
    }
    return Vector;
}

//main function
int main(int argc, char **argv)
{
    std::string InitCard = "SimuCard.yml";
    bool UseDefaultCard = true;
    G4UIExecutive *ui = NULL;
    // Add arglist control
    PrintUsage();
    if (argc >= 2)
    {
        for(int iA = 1; iA < argc; ++iA)
        {
            if(std::string(argv[iA])[0] == std::string("-"))
            {
                if(std::string(argv[iA]) == std::string("-v"))
                {
                    G4cout << " Use interactive mode" << G4endl;
                    ui = new G4UIExecutive(argc, argv);
                }
                if(std::string(argv[iA]) == std::string("-s"))
                {
                    bashComs.RandomSeed = std::stoi(argv[iA+1]);
                }
                if(std::string(argv[iA]) == std::string("-o"))
                {
                    bashComs.OutputFile = std::string(argv[iA+1]);
                }
                if(std::string(argv[iA]) == std::string("-x"))
                {
                    bashComs.DetectorGDML = std::string(argv[iA+1]);
                }
            }
            else if(SplitStr(argv[iA], "\\.").back() == std::string("yml"))
            {
                InitCard = std::string(argv[iA]);
                UseDefaultCard = false;
            }
        }
    }
    if(UseDefaultCard)
    {
        G4cout << "Warning!!! Using default " << InitCard << G4endl;
        if (!IfFileExist(InitCard))
        {
            G4cerr << "Error!!! Initialize card \" " << InitCard << " \" NOT FOUND!!!" << G4endl;
            return 1;
        }
    }

    //Load Yaml file
    YAML::Node yamlConfig = YAML::LoadFile(InitCard);

    //Set Particle Source
    MyGenContainer::GetInstance().Config(yamlConfig["Generator"]);

    //Set Random Seed
    G4int RandomSeed = yamlConfig["RandomSeed"].as<int>(2022);
    if(bashComs.RandomSeed != -1)
    {
        RandomSeed = bashComs.RandomSeed;
    }
    G4cout << "Set Random Seed to: " << RandomSeed << G4endl;
    G4Random::setTheEngine(new CLHEP::RanecuEngine);
    G4Random::setTheSeed(RandomSeed);
    gRandom->SetSeed(RandomSeed);

    // Detector construction
    //G4RunManager *runManager = new G4RunManager();
    MyRunManager *runManager = new MyRunManager();
    std::string GDMLFile = yamlConfig["GDML"].as<std::string>("gdml/main.gdml");
    if(bashComs.DetectorGDML != "")
    {
        GDMLFile = bashComs.DetectorGDML;
    }
    MyDetectorConstruction *detector = new MyDetectorConstruction(GDMLFile);
    runManager->SetUserInitialization(detector);

    // PhysicsList
    runManager->SetUserInitialization(new MyPhysicsList(detector));
    //runManager->SetUserInitialization(new QBBC);

    //Set Output info
    G4bool ifValid = yamlConfig["ValidEvent"].as<bool>(false);
    MyAnalysisManager *analysisManager = MyAnalysisManager::GetInstance();
    if(bashComs.OutputFile != "")
    {
        yamlConfig["Output"]["FileName"] = bashComs.OutputFile;
    }
    analysisManager->SetOutputInfo(yamlConfig["Output"], ifValid);

    // User action initialization
    runManager->SetUserInitialization(new MyActionInitialization(detector));
    runManager->Initialize();

    // Initialize visualization
    G4VisManager *visManager = NULL;
    // Get the pointer to the User Interface manager
    G4UImanager *UImanager = G4UImanager::GetUIpointer();

    if (!ui) // batch mode
    {
        runManager->BeamOn(yamlConfig["Generator"]["EventNumber"].as<int>(100), ifValid);
    }
    else // interactive mode
    {
        visManager = new G4VisExecutive;
        visManager->Initialize();
        UImanager->ApplyCommand("/control/execute vis.mac");
        ui->SessionStart();
        delete ui;
        delete visManager;
    }

    delete runManager;
}
